﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using System.Xml;
using Tomahawk.Resources.Reflection;

namespace Tomahawk.Resources.Serialization
{

    /// <summary>
    /// Serializable object list
    /// </summary>
    public class SerializableList<T> : List<T>, IXmlSerializable
    {
        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
        }

        private void readToElement(XmlReader xmlR)
        {
            while (xmlR.NodeType != XmlNodeType.Element) { xmlR.Read(); }
        }

        public void ReadXml(XmlReader reader)
        {
            if (reader.NodeType != XmlNodeType.Element) this.readToElement(reader);

            //read the header, and determine if it was empty
            bool wasEmpty = reader.IsEmptyElement;
            reader.Read();

            //if it was empty, return
            if (wasEmpty) return;

            while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
            {
                if (reader.NodeType == System.Xml.XmlNodeType.Element)
                {
                    //get the propper type from the type attribute, or use (T) if none is specified
                    Type tType = getItemType(reader);

                    if (SerialBoost.ForceAllUseXmlSerializer)
                    {
                        this.readUsingXmlSerialize(reader, tType);
                    }
                    else
                    {
                        IBoostSerializer bs = SerialBoost.Instance.getBoostSerializer(tType);
                        if (bs == null)
                        {
                            this.readUsingXmlSerialize(reader, tType);
                        }
                        else
                        {
                            T target = (T)bs.CreateInstance();
                            bs.DeserializeInner(reader, target);
                            this.Add(target);
                            
                            //advance to next element
                            reader.Read();
                        }
                    }
                }
                reader.MoveToContent();
            }
            reader.ReadEndElement();
        }

        private void readUsingXmlSerialize(System.Xml.XmlReader reader, Type tType)
        {
            //deserialize the array item
            XmlSerializer valueSerializer = new XmlSerializer(tType);
            T value = (T)valueSerializer.Deserialize(reader);
            this.Add(value);
        }

        private static Type getItemType(System.Xml.XmlReader reader)
        {
            string sType = reader.GetAttribute("type");
            Type tType = null;
            if ((sType == null) || (sType == ""))
            {
                tType = typeof(T);
            }
            else
            {
                //try getting the type from all loaded assemblies
                tType = AssemblyIndexer.Instance.GetTypeFromAssemblies(sType);

                //couldn't get the type, try the expected one
                if (tType == null) tType = typeof(T);
            }
            return tType;
        }

        public void WriteXml(System.Xml.XmlWriter writer)
        {
            foreach (T value in this)
            {
                //by default serialize as the base class
                Type tType = typeof(T);

                //but if concrete class supports ISerializableDerivableClass, use the concrete type instead
                if (typeof(ISerializableDerivableType).IsAssignableFrom(tType)) tType = value.GetType();

                //serialize 
                SerialBoost.Instance.SerializeToWriter(value, writer);
            }
        }

    }

}
